package com.m4f.business.service;

import javax.jdo.PersistenceManager;
import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
import com.m4f.utils.StackTraceUtil;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;
import javax.jdo.Query;
import com.m4f.video.exception.VideoNotFoundException;
import javax.jdo.Transaction;
import com.google.gdata.data.youtube.FormUploadToken;
import com.google.gdata.data.youtube.VideoEntry;
import com.google.gdata.data.youtube.YtPublicationState;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.ServiceException;
import com.m4f.business.model.AdditionalInformation;
import com.m4f.business.model.Company;
import com.m4f.business.model.CompanyInfo;
import com.m4f.business.model.Participant;
import com.m4f.business.model.Video;
import com.m4f.business.persistence.PMF;
import com.m4f.business.service.exception.VideoNotRemovableException;
import com.m4f.business.service.helper.FormUploadInfo;
import com.m4f.business.service.ifc.ICompanyService;
import com.m4f.docs.ifc.IDocumentalService;
import com.m4f.video.ifc.IVideoService;

public class CompanyService implements ICompanyService {
	
	private static final Logger LOGGER = Logger.getLogger(CompanyService.class.getName());
	private static final String PROJECT_SUMMARY_DOCUMENT_NAME = "project-sheet.doc";
	private static final String DEFAULT_FOLDER_NAME = "default";
	private IDocumentalService documentalService;
	private IVideoService videoService;
	
	public CompanyService(IDocumentalService gDS, IVideoService vS) {
		this.documentalService = gDS;
		this.videoService = vS;
	}
	
	public Company addCompany(Participant participant) 
		throws Exception {
		Company company = new Company();
		company.setParticipantInfo(participant);
		company.setValidated(false);
		company.setPreselected(false);
		company.setCreatedDate(Calendar.getInstance().getTime());
		company.setUpdatedDate(Calendar.getInstance().getTime());
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
            pm.makePersistent(company);
            
        } catch(Exception e) {
        	e.printStackTrace();
        	LOGGER.severe("Error in addCompany: " + 
	    			e.getMessage());
        } finally {
        	this.createCompanyDocumentalContainer(company);
            pm.close();
        }
        return company;
	}
	
	
	
	public boolean existCompany(Participant participant) {
		Company company = this.
			getCompanyByParticipant(participant.getEmail());
		return company==null?false:true;
	}
	
	public Company addVideoToCompany(Company company, String videoId) 
		throws IOException, ServiceException, VideoNotFoundException {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		this.loadVideo(company, videoId);
		company.setValidated(false);
		company.setUpdatedDate(Calendar.getInstance().getTime());
		try {
            pm.makePersistent(company);
        } catch(Exception e) {
        	LOGGER.severe("Error in addCompany: " + 
	    			e.getMessage());
        } finally {
            pm.close();
        }
		return company;
	}
	
	public void refreshCompanyVideoInformation(Company company) 
		throws IOException, ServiceException, VideoNotFoundException {
		if(company.getVideos().size()==0) {
			return;
		}
		Video video = company.getVideos().get(0);
		this.loadVideo(company, video.getYouTubeId());
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
            pm.makePersistent(company);
        } catch(Exception e) {
        	LOGGER.severe("Error in addCompany: " + 
	    			e.getMessage());
        } finally {
            pm.close();
        }
	}
	
	
	
	public Company validateCompany(Company company) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		company.setValidated(true);
		try {
            pm.makePersistent(company);
        } catch(Exception e) {
        	LOGGER.severe("Error in addCompany: " + 
	    			e.getMessage());
        } finally {
            pm.close();
        }
        return company;
	}
	
	public Company blockCompany(Company company) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		company.setValidated(false);
		try {
            pm.makePersistent(company);
        } catch(Exception e) {
        	LOGGER.severe("Error in addCompany: " + 
	    			e.getMessage());
        } finally {
            pm.close();
        }
        return company;
	}
	
	public Company preselectCompany(Company company) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		company.setPreselected(true);
		try {
            pm.makePersistent(company);
        } catch(Exception e) {
        	LOGGER.severe("Error in addCompany: " + 
	    			e.getMessage());
        } finally {
            pm.close();
        }
        return company;
	}
	
	public Company unPreselectCompany(Company company) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		company.setPreselected(false);
		try {
            pm.makePersistent(company);
        } catch(Exception e) {
        	LOGGER.severe("Error in addCompany: " + 
	    			e.getMessage());
        } finally {
            pm.close();
        }
        return company;
	}
	
	public Company finalistCompany(Company company) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		company.setFinalist(true);
		try {
            pm.makePersistent(company);
        } catch(Exception e) {
        	LOGGER.severe("Error in addCompany: " + 
	    			e.getMessage());
        } finally {
            pm.close();
        }
        return company;
	}
	
	public Company unFinalistCompany(Company company) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		company.setFinalist(false);
		try {
            pm.makePersistent(company);
        } catch(Exception e) {
        	LOGGER.severe("Error in addCompany: " + 
	    			e.getMessage());
        } finally {
            pm.close();
        }
        return company;
	}
	
	public void addAdditionalInformation(Company company, 
			AdditionalInformation additionalInformation) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		company.setAdditionalInformation(additionalInformation);
		try {
            pm.makePersistent(company);
        } catch(Exception e) {
        	LOGGER.severe("Error in addAdditionalInformation: " + 
	    			e.getMessage());
        } finally {
            pm.close();
        }
	}
	
	public boolean removeCompany(Company company) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Transaction tx = pm.currentTransaction();
		try {
			tx.begin();
			List<String> videoIds = new ArrayList<String>();
			for(Video video : company.getVideos()) {
				videoIds.add(video.getYouTubeId());
			}
			pm.deletePersistent(company);
			tx.commit();
			for(String videoId : videoIds) {
				if(!this.videoService.deleteVideo(videoId)) {
					LOGGER.severe("Video with id " + videoId + 
							" can not been removed.");
				}
			}
			return true;
		} catch(Exception e) {
	    	LOGGER.severe("Error removing company with id (" + 
	    			company.getId()+ "): " + e.getMessage());
	    } finally {
	    	if (tx.isActive()) {
	            tx.rollback();
	        }
	    	pm.close();
	    }
	    return false;
	}
	
	public Company saveCompany(Company company) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		company.setUpdatedDate(Calendar.getInstance().getTime());
		Company compBis = this.getCompanyById(company.getId());
		company.setAdditionalInformation(compBis.getAdditionalInformation());
		try {
            pm.makePersistent(company);
        } catch(Exception e) {
        	LOGGER.severe("Error in addCompany: " + 
        			StackTraceUtil.getStackTrace(e));
        } finally {
            pm.close();
        }
        return company;
	}
	
	public Company getCompanyByParticipant(String participantEmail) {
		Company company = null;
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query query = pm.newQuery(Company.class);
		query.setFilter("participantInfo.email == emailParam");
		query.declareParameters("java.lang.String emailParam");
		List<Company> results = new ArrayList<Company>();
		try {
	        results.addAll((List<Company>)query.execute(participantEmail));
	        if(results.size()>0) {
	        	company = results.get(0);
	        	company = pm.detachCopy(company);
	        }
	    } catch(Exception e) {
	    	LOGGER.severe("Error in getCompanyByParticipant: " + 
	    			e.getMessage());
	    } finally {
	        query.closeAll();
	        pm.close();
	    }
	    return company;
	}
	
	public Company getCompanyById(Long id) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Company company = null;
		try {
			company = pm.getObjectById(Company.class, id);
		} catch(Exception e) {
			LOGGER.severe("Error loading video with id ( " + 
					id + "): " + e.getMessage());
			e.printStackTrace();
		} finally {
	        pm.close();
	    }
		return company;
	}
	
	public List<Company> getAllCompanies() {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query query = pm.newQuery(Company.class);
		List<Company> results = new ArrayList<Company>();
		try {
	        results.addAll((List<Company>)query.execute());
	    } catch(Exception e) {
	    	LOGGER.severe("Error in getAllCompanies: " + 
	    			e.getMessage());
	    } finally {
	        query.closeAll();
	    }
	    return results;
	}
	
	public List<Company> getCompanies(Boolean validated) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query query = pm.newQuery(Company.class);
		query.setFilter("validated == validatedParam");
		query.declareParameters("java.lang.Boolean validatedParam");
		List<Company> results = new ArrayList<Company>();
		try {
	        results.addAll((List<Company>)query.execute(validated));
	    } catch(Exception e) {
	    	LOGGER.severe("Error in getAllCompanies: " + 
	    			e.getMessage());
	    } finally {
	        query.closeAll();
	    }
	    return results;
	}
	
	public List<Company> getCompanies() {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query query = pm.newQuery(Company.class);
		List<Company> results = new ArrayList<Company>();
		try {
	        results.addAll((List<Company>)query.execute());
	    } catch(Exception e) {
	    	LOGGER.severe("Error in getAllCompanies: " + 
	    			e.getMessage());
	    } finally {
	        query.closeAll();
	    }
	    return results;
	}
	
	public boolean removeAllCompanies() {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query query = pm.newQuery(Company.class);
		List<Company> results = new ArrayList<Company>();
		boolean operationOK = true;
		try {
			results.addAll((List<Company>)query.execute());
			for(Company company : results) {
				try {
					LOGGER.info("***Deleting company with id " + company.getId());
					pm.deletePersistent(company);
				} catch(Exception e) {
					LOGGER.severe(StackTraceUtil.getStackTrace(e));
			    	operationOK = false;
			    }
			}
	    } catch(Exception e) {
	    	LOGGER.severe(StackTraceUtil.getStackTrace(e));
	    	operationOK = false;
	    } finally {
	        query.closeAll();
	        pm.close();
	    }
	    return operationOK;
	}
	
	
	public List<Company> getPreselectedCompanies() {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query query = pm.newQuery(Company.class);
		query.setFilter("preselected == preselectedParam");
		query.declareParameters("java.lang.Boolean preselectedParam");
		List<Company> results = new ArrayList<Company>();
		try {
	        results.addAll((List<Company>)query.execute(true));
	    } catch(Exception e) {
	    	LOGGER.severe(StackTraceUtil.getStackTrace(e));
	    } finally {
	        query.closeAll();
	    }
	   
	    return results;
	}
	
	public List<Company> getFinalistCompanies() {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Query query = pm.newQuery(Company.class);
		query.setFilter("finalist == finalistParam");
		query.declareParameters("java.lang.Boolean finalistParam");
		query.setOrdering("companyInfo.name asc");
		List<Company> results = new ArrayList<Company>();
		try {
	        results.addAll((List<Company>)query.execute(true));
	    } catch(Exception e) {
	    	LOGGER.severe(StackTraceUtil.getStackTrace(e));
	    } finally {
	        query.closeAll();
	    }
	    return results;
	}
	

	public FormUploadInfo getVideoUploadData(Company company) 
		throws ServiceException, IOException {
		List<String> keywords = new ArrayList<String>();
		keywords.add("ident-" + company.getId().toString());
		String title = "Video of company " + company.getId().toString();
		FormUploadToken uploadToken = this.videoService.startVideoUpload(keywords, title, title, false);
		FormUploadInfo uploadInfo = new FormUploadInfo(uploadToken.getToken(), 
				uploadToken.getUrl());
		return uploadInfo;
	}
	
	
	/******************************************************************************************
	 * 
	 * 								DOCUMENTS MANAGEMENT METHODS
	 * 
	 ******************************************************************************************/
	
	public void downloadProjectSummaryDocument(Company company, OutputStream os) 
		throws MalformedURLException, IOException, ServiceException, Exception {
		if(!this.hasProjectSummaryDocument(company)) {
			this.documentalService.downloadDocument(DEFAULT_FOLDER_NAME, 
					PROJECT_SUMMARY_DOCUMENT_NAME, os);
		} else {
			String documentName = company.getId() + "-" + PROJECT_SUMMARY_DOCUMENT_NAME;
			this.documentalService.downloadDocument("" + company.getId(), 
					documentName, os);
		}	
	}
	
	public void downloadProjectSummaryPDF(Company company, OutputStream os) 
		throws MalformedURLException, IOException, ServiceException, Exception {	
		if(!this.hasProjectSummaryDocument(company)) {
			this.documentalService.downloadPDF(DEFAULT_FOLDER_NAME, 
					PROJECT_SUMMARY_DOCUMENT_NAME, os);
		} else {
			String documentName = company.getId() + "-" + 
				PROJECT_SUMMARY_DOCUMENT_NAME;
			this.documentalService.downloadPDF("" + company.getId(), 
					documentName, os);
		}	
	}
	
	public void updateProjectSummaryDocument(Company company, 
			byte[] content) throws Exception {
		String documentName = company.getId() + "-" + 
			PROJECT_SUMMARY_DOCUMENT_NAME;
		if(this.hasProjectSummaryDocument(company)) {
			this.documentalService.updateDocument("" + company.getId(), 
					documentName , content);
		} else {
			this.documentalService.addDocument("" + company.getId(), 
					documentName , content);
		}
	}
	
	public boolean hasProjectSummaryDocument(Company company) 
		throws IOException, ServiceException {
		String documentName = company.getId() + "-" + 
			PROJECT_SUMMARY_DOCUMENT_NAME;
		return this.documentalService.existDocument("" + 
				company.getId(), documentName);
	}
	
	private void createCompanyDocumentalContainer(Company company) 
		throws IOException, ServiceException {
		this.documentalService.createCompanyContainer("" + 
				company.getId());
	}
	
	/********************************************************************************************
	 * 
	 *									VIDEO MANAGEMENT METHODS
	 * @throws ServiceException 
	 * @throws IOException 
	 *
	 ********************************************************************************************/
	
	
	public Company deleteVideoFromCompany(Company company) 
		throws VideoNotRemovableException, IOException, ServiceException {
		//First delete from youtube
		for(Video video : company.getVideos()) {
			if(!this.videoService.deleteVideo(video.getYouTubeId())) {
				/*throw new VideoNotRemovableException("Video " + 
						video.getYouTubeId() + " is not removable.");*/
				LOGGER.severe("Video with id " + video.getYouTubeId() + 
						" can not been removed.");
			}
		}
		company.getVideos().removeAll(company.getVideos());
		PersistenceManager pm = PMF.get().getPersistenceManager();
		company.setValidated(false);
		try {
            pm.makePersistent(company);
        } catch(Exception e) {
        	LOGGER.severe("Error in deleteVideoFromCompany: " + 
	    			e.getMessage());
        } finally {
            pm.close();
        }
		return company;
	}
	
	public void updateVideoInformation(Company company) 
		throws MalformedURLException, AuthenticationException, 
		IOException, ServiceException {
		if(!company.hasVideo()) {
			return;
		}
		Video video = company.getVideos().get(0);
		if(company.getCompanyInfo()!=null) {
			CompanyInfo companyInfo = company.getCompanyInfo();
			String title = "", description = "";
			if(companyInfo.getName()!=null && !"".equals(companyInfo.getName())) {
				title = "Video de la compañia " + companyInfo.getName();
				description = "Buena práctica en sostenibilidad " + companyInfo.getName();
			} else {
				title = "Video de la compañia " + company.getId();
				description = "Buena práctica en sostenibilidad " + company.getId();
			}
			String location = "";
			if(!"".equals(companyInfo.getCity()) && !"".equals(companyInfo.getCountry())) {
				location = companyInfo.getCity()  + ", " + companyInfo.getCountry();
			}
			List<String> keywords = new ArrayList<String>();
			keywords.add("buena practica");
			keywords.add("sostenibilidad");
			this.videoService.updateVideoData(video.getYouTubeId(), 
				title, description, location, keywords);
		}
	}
	
	public String getVideoUploadErrorMessage(Company company, int errorCode) {
		return this.videoService.getErrorMessage(errorCode);
	}
	
	/*********************************************************************************************
	 * 
	 *										PRIVATE METHODS
	 *
	 *********************************************************************************************/
	
	private void loadVideo(Company company, 
			String videoId) throws IOException, 
		ServiceException, VideoNotFoundException {
		company.getVideos().removeAll(company.getVideos());
		Video video;
		String thumbURL = "";
		video = new Video();
		video.setCompany(company);
		video.setYouTubeId(videoId);
		VideoEntry youtubeVideo = this.videoService.getVideoById(videoId);
			thumbURL = youtubeVideo.getMediaGroup().getThumbnails().
			get(youtubeVideo.getMediaGroup().getThumbnails().size()-1).getUrl();
		LOGGER.info("Thumb UrL: " + thumbURL);
		video.setThumbURL(thumbURL);
		video.setAvailable(true);
		if(youtubeVideo.isDraft()) {
			video.setAvailable(false);
			YtPublicationState pubState = youtubeVideo.getPublicationState();	
			if (pubState.getState() == YtPublicationState.State.PROCESSING) {
				video.setReason("Video is still being processed.");
			} else if (pubState.getState() == YtPublicationState.State.REJECTED) {
				video.setReason("Video has been rejected because: " + 
						pubState.getDescription());
			} else if (pubState.getState() == YtPublicationState.State.FAILED) {
				video.setReason("Video failed uploading because: " + 
						pubState.getDescription());
			}
		}
		video.setDuration(youtubeVideo.getMediaGroup().getDuration());
		video.setUploaded(new Date(youtubeVideo.getMediaGroup().getUploaded().getValue()));	
		company.getVideos().add(video);
	}
}